bitkeeper revision 1.796 (4055ada1c5nV7AgvKi2Y_vrxKyA7CA)
authorkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Mon, 15 Mar 2004 13:20:33 +0000 (13:20 +0000)
committerkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Mon, 15 Mar 2004 13:20:33 +0000 (13:20 +0000)
manager.py, console.py, __init__.py:
  new file
setup.py, utils.c, main.py, Makefile:
  Refactor the Xen daemon.
main.py:
  Rename: tools/xend/xend.py -> tools/xend/lib/main.py
utils.c:
  Rename: tools/xend/xend_utils.c -> tools/xend/lib/utils.c

.rootkeys
tools/xend/Makefile
tools/xend/lib/__init__.py [new file with mode: 0644]
tools/xend/lib/console.py [new file with mode: 0644]
tools/xend/lib/main.py [new file with mode: 0755]
tools/xend/lib/manager.py [new file with mode: 0644]
tools/xend/lib/utils.c [new file with mode: 0644]
tools/xend/setup.py
tools/xend/xend.py [deleted file]
tools/xend/xend_utils.c [deleted file]

index c090dc8fd40c3c0731a20c7436f4953814d8b1a4..55b25752477126caccf093c00e03aedf36b22cb1 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 3fbd4bd6GtGwZGxYUJPOheYIR7bPaA tools/xc/py/XenoUtil.py
 3fbd0a40yT6G3M9hMpaz5xTUdl0E4g tools/xc/py/setup.py
 40431ac64Hj4ixUnKmlugZKhXPFE_Q tools/xend/Makefile
+4055ad95Se-FqttgxollqOAAHB94zA tools/xend/lib/__init__.py
+4055ad97wMLUj0BZT0e_T0EwQN0Bvw tools/xend/lib/console.py
+4054a301VEag2GwrBrFBna5U1BGlLA tools/xend/lib/main.py
+4055ad9ah9IuC3sJT2c_gYIFY5Tw_g tools/xend/lib/manager.py
+40431ac8wrUEj-XM7B8smFtx_HA7lQ tools/xend/lib/utils.c
 4054a2fdkdATEnRw-U7AUlgu-6JiUA tools/xend/setup.py
-4054a301VEag2GwrBrFBna5U1BGlLA tools/xend/xend.py
-40431ac8wrUEj-XM7B8smFtx_HA7lQ tools/xend/xend_utils.c
 403a3edbrr8RE34gkbR40zep98SXbg tools/xentrace/Makefile
 4050c413PhhLNAYk3TEwP37i_iLw9Q tools/xentrace/xentrace.8
 403a3edbVpV2E_wq1zeEkJ_n4Uu2eg tools/xentrace/xentrace.c
index de6aeb39828d3bd185699d467861fe81fcd4fa53..1ea271e80eecc03d1aec450d68825c33c89d9709 100644 (file)
@@ -5,8 +5,7 @@ all:
 install: all
        if [ "$(prefix)" = "" ]; then python setup.py install; \
        else python setup.py install --root "$(prefix)"; fi
-       install --mode=755 xend.py $(prefix)/usr/sbin
-       ln -sf xend.py $(prefix)/usr/sbin/xend
+       install --mode=755 xend $(prefix)/usr/sbin
 
 dist: all
        mkdir -p ../../../../install/lib/python
@@ -14,8 +13,7 @@ dist: all
                install --mode=755 $$i ../../../../install/lib/python/`basename $$i` ; \
        done
        python -c 'import py_compile, sys; py_compile.compile("XenoUtil.py")'
-       install --mode=755 xend.py ../../../../install/sbin
-       ln -sf xend.py ../../../../install/sbin/xend
+       install --mode=755 xend ../../../../install/sbin
 
 clean:
        rm -rf build *.pyc *.pyo *.a *.so *.o *~ *.rpm 
diff --git a/tools/xend/lib/__init__.py b/tools/xend/lib/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tools/xend/lib/console.py b/tools/xend/lib/console.py
new file mode 100644 (file)
index 0000000..1b2874c
--- /dev/null
@@ -0,0 +1,114 @@
+
+#############################################################
+## xend/console.py -- Console-management functions for Xend
+## Copyright (c) 2004, K A Fraser (University of Cambridge)
+#############################################################
+
+import errno, re, os, select, signal, socket, struct, sys
+
+
+##
+## interface:
+##  Each control interface owns an instance of this class, which manages
+##  the current state of the console interface. Normally a console interface
+##  will be one of two state:
+##   LISTENING: listening for a connection on TCP port 'self.port'
+##   CONNECTED: sending/receiving console data on TCP port 'self.port'
+##
+##  A dictionary of all active interfaces, indexed by TCP socket descriptor,
+##  is accessible as 'interface.interface_list'.
+##
+##  NB. When a class instance is to be destroyed you *must* call the 'close'
+##  method. Otherwise a stale reference will eb left in the interface list.
+##
+class interface:
+
+    # The various states that a console interface may be in.
+    CLOSED    = 0 # No console activity
+    LISTENING = 1 # Listening on port 'self.port'. Socket object 'self.sock'.
+    CONNECTED = 2 # Active connection on 'self.port'. Socket obj 'self.sock'.
+
+
+    # Dictionary of all active (non-closed) console interfaces.
+    interface_list = {}
+
+
+    # NB. 'key' is an opaque value that has no meaning in this class.
+    def __init__(self, port, key):
+        self.status = interface.CLOSED
+        self.port   = port
+        self.key    = key
+
+
+    # Is this interface closed (inactive)?
+    def closed(self):
+        return self.status == interface.CLOSED
+
+
+    # Is this interface listening?
+    def listening(self):
+        return self.status == interface.LISTENING
+
+
+    # Is this interface active and connected?
+    def connected(self):
+        return self.status == interface.CONNECTED
+
+
+    # Close the interface, if it is not closed already.
+    def close(self):
+        if not self.closed():
+            del interface.interface_list[self.sock.fileno()]
+            self.sock.close()
+            del self.sock
+            self.status = interface.CLOSED
+
+
+    # Move the interface into the 'listening' state. Opens a new listening
+    # socket and updates 'interface_list'.
+    def listen(self):
+        # Close old socket (if any), and create a fresh one.
+        self.close()
+        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
+
+        try:
+            # Turn the new socket into a non-blocking listener.
+            self.sock.setblocking(False)
+            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
+                                 struct.pack('ii', 0, 0))
+            self.sock.bind(('', self.port))
+            self.sock.listen(1)
+
+            # Announce the new status of thsi interface.
+            self.status = interface.LISTENING
+            interface.interface_list[self.sock.fileno()] = self
+
+        except:
+            # In case of trouble ensure we get rid of dangling socket reference
+            self.sock.close()
+            del self.sock
+            raise
+
+
+    # Move a listening interface into the 'connected' state.
+    def connect(self):
+        # Pick up a new connection, if one is available.
+        try:
+            (sock, addr) = self.sock.accept()
+        except:
+            return 0
+        sock.setblocking(False)
+        sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
+                        struct.pack('ii', 0, 0))
+
+        # Close the listening socket.
+        self.sock.close()
+
+        # Publish the new socket and the new interface state.
+        self.sock = sock
+        self.status = interface.CONNECTED
+        interface.interface_list[self.sock.fileno()] = self
+        return 1
+
+
diff --git a/tools/xend/lib/main.py b/tools/xend/lib/main.py
new file mode 100755 (executable)
index 0000000..2bf1ed2
--- /dev/null
@@ -0,0 +1,294 @@
+
+###########################################################
+## xend.py -- Xen controller daemon
+## Copyright (c) 2004, K A Fraser (University of Cambridge)
+###########################################################
+
+import errno, re, os, pwd, select, signal, socket, struct, sys, tempfile, time
+import xend.console, xend.manager, xend.utils, Xc
+
+
+# The following parameters could be placed in a configuration file.
+PID  = '/var/run/xend.pid'
+LOG  = '/var/log/xend.log'
+USER = 'root'
+CONTROL_DIR  = '/var/run/xend'
+UNIX_SOCK    = 'management_sock' # relative to CONTROL_DIR
+
+
+def daemon_loop():
+    # Could we do this more nicely? The xend.manager functions need access
+    # to this global state to do their work.
+    global control_list, notifier
+
+    # List of all control interfaces, indexed by local event-channel port.
+    control_list = {}
+
+    xc = Xc.new()
+
+    # Ignore writes to disconnected sockets. We clean up differently.
+    signal.signal(signal.SIGPIPE, signal.SIG_IGN)
+
+    # Construct the management interface. This is a UNIX domain socket via
+    # which we receive 'request' datagrams. Each request is a string that
+    # can be eval'ed as a Python statement. Responses can be remotely eval'ed
+    # by the requester to create a Python dictionary of result values.
+    management_interface = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
+    if os.path.exists(CONTROL_DIR+'/'+UNIX_SOCK):
+        os.unlink(CONTROL_DIR+'/'+UNIX_SOCK)
+    management_interface.setblocking(False)
+    management_interface.bind(CONTROL_DIR+'/'+UNIX_SOCK)
+
+    # Interface via which we receive event notifications from other guest
+    # OSes. This interface also allows us to clear/acknowledge outstanding
+    # notifications --- successive notifications for the same channel are
+    # dropped until the first notification is cleared.
+    notifier = xend.utils.notifier()
+
+    ##
+    ## MAIN LOOP
+    ## 
+    while 1:
+
+        # Construct a poll set. We wait on:
+        #  1. Requests on the management interface.
+        #  2. Incoming event-channel notifications.
+        # Furthermore, for each active control interface:
+        #  3. Incoming console data.
+        #  4. Space for outgoing console data (if there is data to send).
+        waitset = select.poll()
+        waitset.register(management_interface, select.POLLIN)
+        waitset.register(notifier, select.POLLIN)
+        for idx, (port, rbuf, wbuf, con_if) in control_list.items():
+            if not con_if.closed():
+                pflags = select.POLLIN
+                if not rbuf.empty() and con_if.connected():
+                    pflags = select.POLLIN | select.POLLOUT
+                waitset.register(con_if.sock.fileno(), pflags)
+
+        # Wait for something to do...
+        fdset = waitset.poll()
+        
+        # Look for messages on the management interface.
+        # These should consist of executable Python statements that call
+        # well-known management functions (e.g., new_control_interface(dom=9)).
+        try:
+            data, addr = management_interface.recvfrom(2048)
+        except socket.error, error:
+            if error[0] != errno.EAGAIN:
+                raise
+        else:
+            if addr:
+                # Evaluate the request in an exception-trapping sandbox.
+                try:
+                    print "Mgmt_req[%s]: %s" % (addr, data)
+                    response = str(eval('xend.manager.'+data))
+
+                except:
+                    # Catch all exceptions and turn into an error response:
+                    #  status:          False
+                    #  error_type:      'exception'
+                    #  exception_type:  name of exception type.
+                    #  exception value: textual exception value.
+                    exc_type, exc_val = sys.exc_info()[:2]
+                    response = { 'success': False }
+                    response['error_type'] = 'exception'
+                    response['exception_type'] = str(exc_type)
+                    response['exception_value'] = str(exc_val)
+                    response = str(response)
+
+                # Try to send a response to the requester.
+                try:
+                    print "Mgmt_rsp[%s]: %s" % (addr, response)
+                    management_interface.sendto(response, addr)
+                except socket.error, error:
+                    pass
+                
+        # Do work for every console interface that hit in the poll set.
+        for (fd, events) in fdset:
+            if not xend.console.interface.interface_list.has_key(fd):
+                continue
+            con_if = xend.console.interface.interface_list[fd]
+
+            # If the interface is listening, check for pending connections.
+            if con_if.listening():
+                con_if.connect()
+
+            # All done if the interface is not connected.
+            if not con_if.connected():
+                continue
+            (port, rbuf, wbuf, con_if) = control_list[con_if.key]
+
+            # Send as much pending data as possible via the socket.
+            while not rbuf.empty():
+                try:
+                    bytes = con_if.sock.send(rbuf.peek())
+                    if bytes > 0:
+                        rbuf.discard(bytes)
+                except socket.error, error:
+                    pass
+
+            # Read as much data as is available. Don't worry about
+            # overflowing our buffer: it's more important to read the
+            # incoming data stream and detect errors or closure of the
+            # remote end in a timely manner.
+            try:
+                while 1:
+                    data = con_if.sock.recv(2048)
+                    # Return of zero means the remote end has disconnected.
+                    # We therefore return the console interface to listening.
+                    if not data:
+                        con_if.listen()
+                        break
+                    wbuf.write(data)
+            except socket.error, error:
+                # Assume that most errors mean that the connection is dead.
+                # In such cases we return the interface to 'listening' state.
+                if error[0] != errno.EAGAIN:
+                    print "Better return to listening"
+                    con_if.listen()
+                    print "New status: " + str(con_if.status)
+
+            # We may now have pending data to send via the relevant
+            # inter-domain control interface. If so then we send all we can
+            # and notify the remote end.
+            work_done = False
+            while not wbuf.empty() and port.space_to_write_request():
+                msg = xend.utils.message(0, 0, 0)
+                msg.append_payload(wbuf.read(msg.MAX_PAYLOAD))
+                port.write_request(msg)
+                work_done = True
+            if work_done:
+                port.notify()
+
+        # Process control-interface notifications from other guest OSes.
+        while 1:            
+            # Grab a notification, if there is one.
+            notification = notifier.read()
+            if not notification:
+                break
+            (idx, type) = notification
+
+            # If we pick up a disconnect notification then we do any necessary
+            # cleanup, even if the event channel doesn't belong to us.
+            # This is intended to prevent the event-channel port space from
+            # getting clogged with stale connections.
+            if type == notifier.DISCONNECT:
+                ret = xc.evtchn_status(idx)
+                if ret['status'] != 'connected':
+                    notifier.clear(idx, notifier.NORMAL)
+                    notifier.clear(idx, notifier.DISCONNECT)
+                    if control_list.has_key(idx):
+                        (port, rbuf, wbuf, con_if) =  control_list[idx]
+                        con_if.close()
+                        del control_list[idx], port, rbuf, wbuf, con_if
+                    elif ret['status'] == 'disconnected':
+                        # There's noone to do the closure for us...
+                        xc.evtchn_close(idx)
+
+            # A standard notification: probably means there are messages to
+            # read or that there is space to write messages.
+            elif type == notifier.NORMAL and control_list.has_key(idx):
+                (port, rbuf, wbuf, con_if) = control_list[idx]
+                work_done = False
+
+                # We clear the notification before doing any work, to avoid
+                # races.
+                notifier.clear(idx, notifier.NORMAL)
+
+                # Read incoming requests. Currently assume that request
+                # message always containb console data.
+                while port.request_to_read():
+                    msg = port.read_request()
+                    rbuf.write(msg.get_payload())
+                    port.write_response(msg)
+                    work_done = True
+
+                # Incoming responses are currently thrown on the floor.
+                while port.response_to_read():
+                    msg = port.read_response()
+                    work_done = True
+
+                # Send as much pending console data as there is room for.
+                while not wbuf.empty() and port.space_to_write_request():
+                    msg = xend.utils.message(0, 0, 0)
+                    msg.append_payload(wbuf.read(msg.MAX_PAYLOAD))
+                    port.write_request(msg)
+                    work_done = True
+
+                # Finally, notify the remote end of any work that we did.
+                if work_done:
+                    port.notify()
+
+
+
+def cleanup_daemon(kill=False):
+    # No cleanup to do if the PID file is empty.
+    if not os.path.isfile(PID) or not os.path.getsize(PID):
+        return 0
+    # Read the PID of the previous invocation and search active process list.
+    pid = open(PID, 'r').read()
+    lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines()
+    for line in lines:
+        if re.search('^ *' + pid + '.+xend', line):
+            if not kill:
+                print "Daemon is already running (PID %d)" % int(pid)
+                return 1
+            # Old daemon is still active: terminate it.
+            os.kill(int(pid), 1)
+    # Delete the, now stale, PID file.
+    os.remove(PID)
+    return 0
+
+
+
+def start_daemon():
+    if cleanup_daemon(kill=False):
+        return 1
+
+    if not os.path.exists(CONTROL_DIR):
+        os.mkdir(CONTROL_DIR)
+
+    # Open log file. Truncate it if non-empty, and request line buffering.
+    if os.path.isfile(LOG):
+        os.rename(LOG, LOG+'.old')
+    logfile = open(LOG, 'w+', 1)
+
+    # Detach from TTY.
+    os.setsid()
+
+    # Set the UID.
+    try:
+        os.setuid(pwd.getpwnam(USER)[2])
+    except KeyError, error:
+        print "Error: no such user '%s'" % USER
+        return 1
+
+    # Ensure that zombie children are automatically reaped.
+    xend.utils.autoreap()
+
+    # Fork -- parent writes the PID file and exits.
+    pid = os.fork()
+    if pid:
+        pidfile = open(PID, 'w')
+        pidfile.write(str(pid))
+        pidfile.close()
+        return 0
+
+    # Close down standard file handles
+    try:
+        os.close(0) # stdin
+        os.close(1) # stdout
+        os.close(2) # stderr
+    except:
+        pass
+
+    # Redirect output to log file, then enter the main loop.
+    sys.stdout = sys.stderr = logfile
+    daemon_loop()
+    return 0
+
+
+
+def stop_daemon():
+    return cleanup_daemon(kill=True)
diff --git a/tools/xend/lib/manager.py b/tools/xend/lib/manager.py
new file mode 100644 (file)
index 0000000..092caa0
--- /dev/null
@@ -0,0 +1,39 @@
+
+#############################################################
+## xend/manager.py -- Management-interface functions for Xend
+## Copyright (c) 2004, K A Fraser (University of Cambridge)
+#############################################################
+
+import xend.console, xend.main, xend.utils
+
+
+##
+## new_control_interface:
+##  Create a new control interface with the specified domain 'dom'.
+##  The console port may also be specified; otehrwise a suitable port is
+##  automatically allocated.
+##
+def new_control_interface(dom, console_port=-1):
+    # Allocate an event channel. Clear pending notifications.
+    port = xend.utils.port(dom)
+    xend.main.notifier.clear(port.local_port, xend.main.notifier.NORMAL)
+    xend.main.notifier.clear(port.local_port, xend.main.notifier.DISCONNECT)
+    
+    # If necessary, compute a suitable TCP port for console I/O.
+    if console_port < 0:
+        console_port = 9600 + port.local_port
+
+    # Create a listenign console interface.
+    con_if = xend.console.interface(console_port, port.local_port)
+    con_if.listen()
+
+    # Add control state to the master list.
+    xend.main.control_list[port.local_port] = \
+      (port, xend.utils.buffer(), xend.utils.buffer(), con_if)
+
+    # Construct the successful response to be returned to the requester.
+    response = { 'success': True }
+    response['local_port']   = port.local_port
+    response['remote_port']  = port.remote_port
+    response['console_port'] = console_port
+    return response
diff --git a/tools/xend/lib/utils.c b/tools/xend/lib/utils.c
new file mode 100644 (file)
index 0000000..de832a1
--- /dev/null
@@ -0,0 +1,1006 @@
+/******************************************************************************
+ * utils.c
+ * 
+ * Copyright (c) 2004, K A Fraser
+ */
+
+#include <Python.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <xc.h>
+#include <asm-xeno/control_if.h>
+
+/* NB. The following should be kept in sync with the kernel's evtchn driver. */
+#define EVTCHN_DEV_NAME  "/dev/xen/evtchn"
+#define EVTCHN_DEV_MAJOR 10
+#define EVTCHN_DEV_MINOR 200
+#define PORT_NORMAL     0x0000   /* A standard event notification.      */ 
+#define PORT_DISCONNECT 0x8000   /* A port-disconnect notification.     */
+#define PORTIDX_MASK    0x7fff   /* Strip subtype to obtain port index. */
+#define EVTCHN_RESET _IO('E', 1) /* Clear notification buffer. Clear errors. */
+
+/* Size of a machine page frame. */
+#define PAGE_SIZE 4096
+
+
+/*
+ * *********************** NOTIFIER ***********************
+ */
+
+typedef struct {
+    PyObject_HEAD;
+    int evtchn_fd;
+} xu_notifier_object;
+
+static PyObject *xu_notifier_read(PyObject *self, PyObject *args)
+{
+    xu_notifier_object *xun = (xu_notifier_object *)self;
+    u16 v;
+    int bytes;
+
+    if ( !PyArg_ParseTuple(args, "") )
+        return NULL;
+    
+    while ( (bytes = read(xun->evtchn_fd, &v, sizeof(v))) == -1 )
+    {
+        if ( errno == EINTR )
+            continue;
+        if ( errno == EAGAIN )
+            goto none;
+        return PyErr_SetFromErrno(PyExc_IOError);
+    }
+    
+    if ( bytes == sizeof(v) )
+        return Py_BuildValue("(i,i)", v&PORTIDX_MASK, v&~PORTIDX_MASK);
+
+ none:
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *xu_notifier_clear(PyObject *self, PyObject *args)
+{
+    xu_notifier_object *xun = (xu_notifier_object *)self;
+    u16 v;
+    int idx, type;
+
+    if ( !PyArg_ParseTuple(args, "ii", &idx, &type) )
+        return NULL;
+    
+    v = (u16)idx | (u16)type;
+
+    (void)write(xun->evtchn_fd, &v, sizeof(v));
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *xu_notifier_fileno(PyObject *self, PyObject *args)
+{
+    xu_notifier_object *xun = (xu_notifier_object *)self;
+    return PyInt_FromLong(xun->evtchn_fd);
+}
+
+static PyMethodDef xu_notifier_methods[] = {
+    { "read",
+      (PyCFunction)xu_notifier_read,
+      METH_VARARGS,
+      "Read a (@port, @type) pair.\n" },
+
+    { "clear", 
+      (PyCFunction)xu_notifier_clear,
+      METH_VARARGS,
+      "Clear a (@port, @type) pair.\n" },
+
+    { "fileno", 
+      (PyCFunction)xu_notifier_fileno,
+      METH_VARARGS,
+      "Return the file descriptor for the notification channel.\n" },
+
+    { NULL, NULL, 0, NULL }
+};
+
+staticforward PyTypeObject xu_notifier_type;
+
+static PyObject *xu_notifier_new(PyObject *self, PyObject *args)
+{
+    xu_notifier_object *xun;
+
+    if ( !PyArg_ParseTuple(args, "") )
+        return NULL;
+
+    xun = PyObject_New(xu_notifier_object, &xu_notifier_type);
+
+    xun->evtchn_fd = open(EVTCHN_DEV_NAME, O_NONBLOCK|O_RDWR);
+    if ( xun->evtchn_fd == -1 )
+    {
+        PyObject_Del((PyObject *)xun);
+        return PyErr_SetFromErrno(PyExc_IOError);
+    }
+
+    return (PyObject *)xun;
+}
+
+static PyObject *xu_notifier_getattr(PyObject *obj, char *name)
+{
+    if ( strcmp(name, "DISCONNECT") == 0 )
+        return PyInt_FromLong(PORT_DISCONNECT);
+    if ( strcmp(name, "NORMAL") == 0 )
+        return PyInt_FromLong(PORT_NORMAL);
+    return Py_FindMethod(xu_notifier_methods, obj, name);
+}
+
+static void xu_notifier_dealloc(PyObject *self)
+{
+    xu_notifier_object *xun = (xu_notifier_object *)self;
+    (void)close(xun->evtchn_fd);
+    PyObject_Del(self);
+}
+
+static PyTypeObject xu_notifier_type = {
+    PyObject_HEAD_INIT(&PyType_Type)
+    0,
+    "notifier",
+    sizeof(xu_notifier_object),
+    0,
+    xu_notifier_dealloc, /* tp_dealloc     */
+    NULL,                /* tp_print       */
+    xu_notifier_getattr, /* tp_getattr     */
+    NULL,                /* tp_setattr     */
+    NULL,                /* tp_compare     */
+    NULL,                /* tp_repr        */
+    NULL,                /* tp_as_number   */
+    NULL,                /* tp_as_sequence */
+    NULL,                /* tp_as_mapping  */
+    NULL                 /* tp_hash        */
+};
+
+
+
+/*
+ * *********************** MESSAGE ***********************
+ */
+
+typedef struct {
+    PyObject_HEAD;
+    control_msg_t msg;
+} xu_message_object;
+
+static PyObject *xu_message_append_payload(PyObject *self, PyObject *args)
+{
+    xu_message_object *xum = (xu_message_object *)self;
+    char *str;
+    int len;
+
+    if ( !PyArg_ParseTuple(args, "s#", &str, &len) )
+        return NULL;
+
+    if ( (len + xum->msg.length) > sizeof(xum->msg.msg) )
+    {
+        PyErr_SetString(PyExc_RuntimeError, "out of space in control message");
+        return NULL;
+    }
+
+    memcpy(&xum->msg.msg[xum->msg.length], str, len);
+    xum->msg.length += len;
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *xu_message_get_payload(PyObject *self, PyObject *args)
+{
+    xu_message_object *xum = (xu_message_object *)self;
+
+    if ( !PyArg_ParseTuple(args, "") )
+        return NULL;
+
+    return PyString_FromStringAndSize(xum->msg.msg, xum->msg.length);
+}
+
+static PyObject *xu_message_set_header(PyObject *self, 
+                                       PyObject *args, 
+                                       PyObject *kwds)
+{
+    xu_message_object *xum = (xu_message_object *)self;
+    int type = -1, subtype = -1, id = -1;
+
+    static char *kwd_list[] = { "type", "subtype", "id", NULL };
+
+    if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|iii", kwd_list,
+                                      &type, &subtype, &id) )
+        return NULL;
+
+    if ( type != -1 )
+        xum->msg.type = (u8)type;
+    if ( subtype != -1 )
+        xum->msg.subtype = (u8)subtype;
+    if ( id != -1 )
+        xum->msg.id = (u8)id;
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *xu_message_get_header(PyObject *self, PyObject *args)
+{
+    xu_message_object *xum = (xu_message_object *)self;
+
+    if ( !PyArg_ParseTuple(args, "") )
+        return NULL;
+
+    return Py_BuildValue("{s:i,s:i,s:i}",
+                         "type",    xum->msg.type,
+                         "subtype", xum->msg.subtype,
+                         "id",      xum->msg.id);
+}
+
+static PyMethodDef xu_message_methods[] = {
+    { "append_payload", 
+      (PyCFunction)xu_message_append_payload,
+      METH_VARARGS,
+      "Append @str to the message payload.\n" },
+
+    { "get_payload",
+      (PyCFunction)xu_message_get_payload,
+      METH_VARARGS,
+      "Return the message payload in string form.\n" },
+
+    { "set_header",
+      (PyCFunction)xu_message_set_header,
+      METH_VARARGS | METH_KEYWORDS,
+      "Accepts keywords @type, @subtype, and @id.\n" },
+
+    { "get_header",
+      (PyCFunction)xu_message_get_header,
+      METH_VARARGS,
+      "Returns a dictionary of values for @type, @subtype, and @id.\n" },
+
+    { NULL, NULL, 0, NULL }
+};
+
+staticforward PyTypeObject xu_message_type;
+
+static PyObject *xu_message_new(PyObject *self, PyObject *args)
+{
+    xu_message_object *xum;
+    int type, subtype, id;
+
+    if ( !PyArg_ParseTuple(args, "iii", &type, &subtype, &id) )
+        return NULL;
+
+    xum = PyObject_New(xu_message_object, &xu_message_type);
+
+    xum->msg.type    = type;
+    xum->msg.subtype = subtype;
+    xum->msg.id      = id;
+    xum->msg.length  = 0;
+
+    return (PyObject *)xum;
+}
+
+static PyObject *xu_message_getattr(PyObject *obj, char *name)
+{
+    xu_message_object *xum;
+    if ( strcmp(name, "MAX_PAYLOAD") == 0 )
+        return PyInt_FromLong(sizeof(xum->msg.msg));
+    return Py_FindMethod(xu_message_methods, obj, name);
+}
+
+static void xu_message_dealloc(PyObject *self)
+{
+    PyObject_Del(self);
+}
+
+static PyTypeObject xu_message_type = {
+    PyObject_HEAD_INIT(&PyType_Type)
+    0,
+    "message",
+    sizeof(xu_message_object),
+    0,
+    xu_message_dealloc,   /* tp_dealloc     */
+    NULL,                /* tp_print       */
+    xu_message_getattr,   /* tp_getattr     */
+    NULL,                /* tp_setattr     */
+    NULL,                /* tp_compare     */
+    NULL,                /* tp_repr        */
+    NULL,                /* tp_as_number   */
+    NULL,                /* tp_as_sequence */
+    NULL,                /* tp_as_mapping  */
+    NULL                 /* tp_hash        */
+};
+
+
+
+/*
+ * *********************** PORT ***********************
+ */
+
+static control_if_t *map_control_interface(int fd, unsigned long pfn)
+{
+    char *vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE,
+                       MAP_SHARED, fd, pfn * PAGE_SIZE);
+    if ( vaddr == MAP_FAILED )
+        return NULL;
+    return (control_if_t *)(vaddr + 2048);
+}
+static void unmap_control_interface(int fd, control_if_t *c)
+{
+    char *vaddr = (char *)c - 2048;
+    (void)munmap(vaddr, PAGE_SIZE);
+}
+
+typedef struct {
+    PyObject_HEAD;
+    int mem_fd;
+    int xc_handle;
+    u64 remote_dom;
+    int local_port, remote_port;
+    control_if_t    *interface;
+    CONTROL_RING_IDX tx_req_cons, tx_resp_prod;
+    CONTROL_RING_IDX rx_req_prod, rx_resp_cons;
+} xu_port_object;
+
+static PyObject *port_error;
+
+static PyObject *xu_port_notify(PyObject *self, PyObject *args)
+{
+    xu_port_object *xup = (xu_port_object *)self;
+
+    if ( !PyArg_ParseTuple(args, "") )
+        return NULL;
+
+    (void)xc_evtchn_send(xup->xc_handle, xup->local_port);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *xu_port_read_request(PyObject *self, PyObject *args)
+{
+    xu_port_object    *xup = (xu_port_object *)self;
+    xu_message_object *xum;
+    CONTROL_RING_IDX   c = xup->tx_req_cons;
+    control_if_t      *cif = xup->interface;
+    control_msg_t     *cmsg;
+
+    if ( !PyArg_ParseTuple(args, "") )
+        return NULL;
+
+    if ( (c == cif->tx_req_prod) || 
+         ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) )
+    {
+        PyErr_SetString(port_error, "no request to read");
+        return NULL;
+    }
+
+    cmsg = &cif->tx_ring[MASK_CONTROL_IDX(c)];
+    xum = PyObject_New(xu_message_object, &xu_message_type);
+    memcpy(&xum->msg, cmsg, sizeof(*cmsg));
+    if ( xum->msg.length > sizeof(xum->msg.msg) )
+        xum->msg.length = sizeof(xum->msg.msg);
+    xup->tx_req_cons++;
+    return (PyObject *)xum;
+}
+
+static PyObject *xu_port_write_request(PyObject *self, PyObject *args)
+{
+    xu_port_object    *xup = (xu_port_object *)self;
+    xu_message_object *xum;
+    CONTROL_RING_IDX   p = xup->rx_req_prod;
+    control_if_t      *cif = xup->interface;
+    control_msg_t     *cmsg;
+
+    if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) )
+        return NULL;
+
+    if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) )
+    {
+        PyErr_SetString(PyExc_TypeError, "expected a xend.utils.message");
+        return NULL;        
+    }
+
+    if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) )
+    {
+        PyErr_SetString(port_error, "no space to write request");
+        return NULL;
+    }
+
+    cmsg = &cif->rx_ring[MASK_CONTROL_IDX(p)];
+    memcpy(cmsg, &xum->msg, sizeof(*cmsg));
+
+    xup->rx_req_prod = cif->rx_req_prod = p + 1;
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *xu_port_read_response(PyObject *self, PyObject *args)
+{
+    xu_port_object    *xup = (xu_port_object *)self;
+    xu_message_object *xum;
+    CONTROL_RING_IDX   c = xup->rx_resp_cons;
+    control_if_t      *cif = xup->interface;
+    control_msg_t     *cmsg;
+
+    if ( !PyArg_ParseTuple(args, "") )
+        return NULL;
+
+    if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) )
+    {
+        PyErr_SetString(port_error, "no response to read");
+        return NULL;
+    }
+
+    cmsg = &cif->rx_ring[MASK_CONTROL_IDX(c)];
+    xum = PyObject_New(xu_message_object, &xu_message_type);
+    memcpy(&xum->msg, cmsg, sizeof(*cmsg));
+    if ( xum->msg.length > sizeof(xum->msg.msg) )
+        xum->msg.length = sizeof(xum->msg.msg);
+    xup->rx_resp_cons++;
+    return (PyObject *)xum;
+}
+
+static PyObject *xu_port_write_response(PyObject *self, PyObject *args)
+{
+    xu_port_object    *xup = (xu_port_object *)self;
+    xu_message_object *xum;
+    CONTROL_RING_IDX   p = xup->tx_resp_prod;
+    control_if_t      *cif = xup->interface;
+    control_msg_t     *cmsg;
+
+    if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) )
+        return NULL;
+
+    if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) )
+    {
+        PyErr_SetString(PyExc_TypeError, "expected a xend.utils.message");
+        return NULL;        
+    }
+
+    if ( p == xup->tx_req_cons )
+    {
+        PyErr_SetString(port_error, "no space to write response");
+        return NULL;
+    }
+
+    cmsg = &cif->tx_ring[MASK_CONTROL_IDX(p)];
+    memcpy(cmsg, &xum->msg, sizeof(*cmsg));
+
+    xup->tx_resp_prod = cif->tx_resp_prod = p + 1;
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *xu_port_request_to_read(PyObject *self, PyObject *args)
+{
+    xu_port_object    *xup = (xu_port_object *)self;
+    CONTROL_RING_IDX   c = xup->tx_req_cons;
+    control_if_t      *cif = xup->interface;
+
+    if ( !PyArg_ParseTuple(args, "") )
+        return NULL;
+
+    if ( (c == cif->tx_req_prod) || 
+         ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) )
+        return PyInt_FromLong(0);
+
+    return PyInt_FromLong(1);
+}
+
+static PyObject *xu_port_space_to_write_request(PyObject *self, PyObject *args)
+{
+    xu_port_object    *xup = (xu_port_object *)self;
+    CONTROL_RING_IDX   p = xup->rx_req_prod;
+
+    if ( !PyArg_ParseTuple(args, "") )
+        return NULL;
+
+    if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) )
+        return PyInt_FromLong(0);
+
+    return PyInt_FromLong(1);
+}
+
+static PyObject *xu_port_response_to_read(PyObject *self, PyObject *args)
+{
+    xu_port_object    *xup = (xu_port_object *)self;
+    CONTROL_RING_IDX   c = xup->rx_resp_cons;
+    control_if_t      *cif = xup->interface;
+
+    if ( !PyArg_ParseTuple(args, "") )
+        return NULL;
+
+    if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) )
+        return PyInt_FromLong(0);
+
+    return PyInt_FromLong(1);
+}
+
+static PyObject *xu_port_space_to_write_response(
+    PyObject *self, PyObject *args)
+{
+    xu_port_object    *xup = (xu_port_object *)self;
+    CONTROL_RING_IDX   p = xup->tx_resp_prod;
+
+    if ( !PyArg_ParseTuple(args, "") )
+        return NULL;
+
+    if ( p == xup->tx_req_cons )
+        return PyInt_FromLong(0);
+
+    return PyInt_FromLong(1);
+}
+
+static PyMethodDef xu_port_methods[] = {
+    { "notify",
+      (PyCFunction)xu_port_notify,
+      METH_VARARGS,
+      "Send a notification to the remote end.\n" },
+
+    { "read_request",
+      (PyCFunction)xu_port_read_request,
+      METH_VARARGS,
+      "Read a request message from the control interface.\n" },
+
+    { "write_request",
+      (PyCFunction)xu_port_write_request,
+      METH_VARARGS,
+      "Write a request message to the control interface.\n" },
+
+    { "read_response",
+      (PyCFunction)xu_port_read_response,
+      METH_VARARGS,
+      "Read a response message from the control interface.\n" },
+
+    { "write_response",
+      (PyCFunction)xu_port_write_response,
+      METH_VARARGS,
+      "Write a response message to the control interface.\n" },
+
+    { "request_to_read",
+      (PyCFunction)xu_port_request_to_read,
+      METH_VARARGS,
+      "Returns TRUE if there is a request message to read.\n" },
+
+    { "space_to_write_request",
+      (PyCFunction)xu_port_space_to_write_request,
+      METH_VARARGS,
+      "Returns TRUE if there is space to write a request message.\n" },
+
+    { "response_to_read",
+      (PyCFunction)xu_port_response_to_read,
+      METH_VARARGS,
+      "Returns TRUE if there is a response message to read.\n" },
+
+    { "space_to_write_response",
+      (PyCFunction)xu_port_space_to_write_response,
+      METH_VARARGS,
+      "Returns TRUE if there is space to write a response message.\n" },
+
+    { NULL, NULL, 0, NULL }
+};
+
+staticforward PyTypeObject xu_port_type;
+
+static PyObject *xu_port_new(PyObject *self, PyObject *args)
+{
+    xu_port_object *xup;
+    u64 dom;
+    int port1, port2;
+    xc_dominfo_t info;
+
+    if ( !PyArg_ParseTuple(args, "L", &dom) )
+        return NULL;
+
+    xup = PyObject_New(xu_port_object, &xu_port_type);
+
+    if ( (xup->mem_fd = open("/dev/mem", O_RDWR)) == -1 )
+    {
+        PyErr_SetString(port_error, "Could not open '/dev/mem'");
+        goto fail1;
+    }
+
+    if ( (xup->xc_handle = xc_interface_open()) == -1 )
+    {
+        PyErr_SetString(port_error, "Could not open Xen control interface");
+        goto fail2;
+    }
+
+    if ( xc_evtchn_open(xup->xc_handle, DOMID_SELF, dom, &port1, &port2) != 0 )
+    {
+        PyErr_SetString(port_error, "Could not open channel to domain");
+        goto fail3;
+    }
+
+    if ( (xc_domain_getinfo(xup->xc_handle, dom, 1, &info) != 1) ||
+         (info.domid != dom) )
+    {
+        PyErr_SetString(port_error, "Failed to obtain domain status");
+        goto fail4;
+    }
+
+    xup->interface = 
+        map_control_interface(xup->mem_fd, info.shared_info_frame);
+    if ( xup->interface == NULL )
+    {
+        PyErr_SetString(port_error, "Failed to map domain control interface");
+        goto fail4;
+    }
+
+    xup->tx_req_cons  = 0;
+    xup->tx_resp_prod = 0;
+    xup->rx_req_prod  = 0;
+    xup->rx_resp_cons = 0;
+    xup->remote_dom   = dom;
+    xup->local_port   = port1;
+    xup->remote_port  = port2;
+
+    return (PyObject *)xup;
+
+    
+ fail4:
+    (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, port1);
+ fail3:
+    (void)xc_interface_close(xup->xc_handle);
+ fail2:
+    (void)close(xup->mem_fd);
+ fail1:
+    PyObject_Del((PyObject *)xup);
+    return NULL;        
+}
+
+static PyObject *xu_port_getattr(PyObject *obj, char *name)
+{
+    xu_port_object *xup = (xu_port_object *)obj;
+    if ( strcmp(name, "local_port") == 0 )
+        return PyInt_FromLong(xup->local_port);
+    if ( strcmp(name, "remote_port") == 0 )
+        return PyInt_FromLong(xup->remote_port);
+    if ( strcmp(name, "remote_dom") == 0 )
+        return PyLong_FromUnsignedLongLong(xup->remote_dom);
+    return Py_FindMethod(xu_port_methods, obj, name);
+}
+
+static void xu_port_dealloc(PyObject *self)
+{
+    xu_port_object *xup = (xu_port_object *)self;
+    unmap_control_interface(xup->mem_fd, xup->interface);
+    (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, xup->local_port);
+    (void)xc_interface_close(xup->xc_handle);
+    (void)close(xup->mem_fd);
+    PyObject_Del(self);
+}
+
+static PyTypeObject xu_port_type = {
+    PyObject_HEAD_INIT(&PyType_Type)
+    0,
+    "port",
+    sizeof(xu_port_object),
+    0,
+    xu_port_dealloc,     /* tp_dealloc     */
+    NULL,                /* tp_print       */
+    xu_port_getattr,     /* tp_getattr     */
+    NULL,                /* tp_setattr     */
+    NULL,                /* tp_compare     */
+    NULL,                /* tp_repr        */
+    NULL,                /* tp_as_number   */
+    NULL,                /* tp_as_sequence */
+    NULL,                /* tp_as_mapping  */
+    NULL                 /* tp_hash        */
+};
+
+
+
+/*
+ * *********************** BUFFER ***********************
+ */
+
+#define BUFSZ 65536
+#define MASK_BUF_IDX(_i) ((_i)&(BUFSZ-1))
+typedef unsigned int BUF_IDX;
+
+typedef struct {
+    PyObject_HEAD;
+    char        *buf;
+    unsigned int prod, cons;
+} xu_buffer_object;
+
+static PyObject *__xu_buffer_peek(xu_buffer_object *xub, int max)
+{
+    PyObject *str1, *str2;
+    int len1, len2, c = MASK_BUF_IDX(xub->cons);
+
+    len1 = xub->prod - xub->cons;
+    if ( len1 > (BUFSZ - c) ) /* clip to ring wrap */
+        len1 = BUFSZ - c;
+    if ( len1 > max )         /* clip to specified maximum */
+        len1 = max;
+    if ( len1 < 0 )           /* sanity */
+        len1 = 0;
+
+    if ( (str1 = PyString_FromStringAndSize(&xub->buf[c], len1)) == NULL )
+        return NULL;
+
+    if ( (len1 < (xub->prod - xub->cons)) && (len1 < max) )
+    {
+        len2 = max - len1;
+        if ( len2 > MASK_BUF_IDX(xub->prod) )
+            len2 = MASK_BUF_IDX(xub->prod);
+        if ( len2 > 0 )
+        {
+            str2 = PyString_FromStringAndSize(&xub->buf[0], len2);
+            if ( str2 == NULL )
+                return NULL;
+            PyString_ConcatAndDel(&str1, str2);
+            if ( str1 == NULL )
+                return NULL;
+        }
+    }
+
+    return str1;
+}
+
+static PyObject *xu_buffer_peek(PyObject *self, PyObject *args)
+{
+    xu_buffer_object *xub = (xu_buffer_object *)self;
+    int max = 1024;
+
+    if ( !PyArg_ParseTuple(args, "|i", &max) )
+        return NULL;
+    
+    return __xu_buffer_peek(xub, max);
+}
+
+static PyObject *xu_buffer_read(PyObject *self, PyObject *args)
+{
+    xu_buffer_object *xub = (xu_buffer_object *)self;
+    PyObject *str;
+    int max = 1024;
+
+    if ( !PyArg_ParseTuple(args, "|i", &max) )
+        return NULL;
+
+    if ( (str = __xu_buffer_peek(xub, max)) != NULL )
+        xub->cons += PyString_Size(str);
+
+    return str;
+}
+
+static PyObject *xu_buffer_discard(PyObject *self, PyObject *args)
+{
+    xu_buffer_object *xub = (xu_buffer_object *)self;
+    int max, len;
+
+    if ( !PyArg_ParseTuple(args, "i", &max) )
+        return NULL;
+
+    len = xub->prod - xub->cons;
+    if ( len > max )
+        len = max;
+    if ( len < 0 )
+        len = 0;
+
+    xub->cons += len;
+
+    return PyInt_FromLong(len);
+}
+
+static PyObject *xu_buffer_write(PyObject *self, PyObject *args)
+{
+    xu_buffer_object *xub = (xu_buffer_object *)self;
+    char *str;
+    int len, len1, len2;
+
+    if ( !PyArg_ParseTuple(args, "s#", &str, &len) )
+        return NULL;
+
+    len1 = len;
+    if ( len1 > (BUFSZ - MASK_BUF_IDX(xub->prod)) )
+        len1 = BUFSZ - MASK_BUF_IDX(xub->prod);
+    if ( len1 > (BUFSZ - (xub->prod - xub->cons)) )
+        len1 = BUFSZ - (xub->prod - xub->cons);
+
+    if ( len1 == 0 )
+        return PyInt_FromLong(0);
+
+    memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[0], len1);
+    xub->prod += len1;
+
+    if ( len1 < len )
+    {
+        len2 = len - len1;
+        if ( len2 > (BUFSZ - MASK_BUF_IDX(xub->prod)) )
+            len2 = BUFSZ - MASK_BUF_IDX(xub->prod);
+        if ( len2 > (BUFSZ - (xub->prod - xub->cons)) )
+            len2 = BUFSZ - (xub->prod - xub->cons);
+        if ( len2 != 0 )
+        {
+            memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[len1], len2);
+            xub->prod += len2;
+            return PyInt_FromLong(len1 + len2);
+        }
+    }
+
+    return PyInt_FromLong(len1);
+}
+
+static PyObject *xu_buffer_empty(PyObject *self, PyObject *args)
+{
+    xu_buffer_object *xub = (xu_buffer_object *)self;
+
+    if ( !PyArg_ParseTuple(args, "") )
+        return NULL;
+
+    if ( xub->cons == xub->prod )
+        return PyInt_FromLong(1);
+
+    return PyInt_FromLong(0);
+}
+
+static PyObject *xu_buffer_full(PyObject *self, PyObject *args)
+{
+    xu_buffer_object *xub = (xu_buffer_object *)self;
+
+    if ( !PyArg_ParseTuple(args, "") )
+        return NULL;
+
+    if ( (xub->prod - xub->cons) == BUFSZ )
+        return PyInt_FromLong(1);
+
+    return PyInt_FromLong(0);
+}
+
+static PyMethodDef xu_buffer_methods[] = {
+    { "peek", 
+      (PyCFunction)xu_buffer_peek,
+      METH_VARARGS,
+      "Peek up to @max bytes from the buffer. Returns a string.\n" },
+
+    { "read", 
+      (PyCFunction)xu_buffer_read,
+      METH_VARARGS,
+      "Read up to @max bytes from the buffer. Returns a string.\n" },
+
+    { "discard", 
+      (PyCFunction)xu_buffer_discard,
+      METH_VARARGS,
+      "Discard up to @max bytes from the buffer. Returns number of bytes.\n" },
+
+    { "write", 
+      (PyCFunction)xu_buffer_write,
+      METH_VARARGS,
+      "Write @string into buffer. Return number of bytes written.\n" },
+
+    { "empty", 
+      (PyCFunction)xu_buffer_empty,
+      METH_VARARGS,
+      "Return TRUE if the buffer is empty.\n" },
+
+    { "full", 
+      (PyCFunction)xu_buffer_full,
+      METH_VARARGS,
+      "Return TRUE if the buffer is full.\n" },
+
+    { NULL, NULL, 0, NULL }
+};
+
+staticforward PyTypeObject xu_buffer_type;
+
+static PyObject *xu_buffer_new(PyObject *self, PyObject *args)
+{
+    xu_buffer_object *xub;
+
+    if ( !PyArg_ParseTuple(args, "") )
+        return NULL;
+
+    xub = PyObject_New(xu_buffer_object, &xu_buffer_type);
+
+    if ( (xub->buf = malloc(BUFSZ)) == NULL )
+    {
+        PyObject_Del((PyObject *)xub);
+        return NULL;
+    }
+
+    xub->prod = xub->cons = 0;
+
+    return (PyObject *)xub;
+}
+
+static PyObject *xu_buffer_getattr(PyObject *obj, char *name)
+{
+    return Py_FindMethod(xu_buffer_methods, obj, name);
+}
+
+static void xu_buffer_dealloc(PyObject *self)
+{
+    xu_buffer_object *xub = (xu_buffer_object *)self;
+    free(xub->buf);
+    PyObject_Del(self);
+}
+
+static PyTypeObject xu_buffer_type = {
+    PyObject_HEAD_INIT(&PyType_Type)
+    0,
+    "buffer",
+    sizeof(xu_buffer_object),
+    0,
+    xu_buffer_dealloc,   /* tp_dealloc     */
+    NULL,                /* tp_print       */
+    xu_buffer_getattr,   /* tp_getattr     */
+    NULL,                /* tp_setattr     */
+    NULL,                /* tp_compare     */
+    NULL,                /* tp_repr        */
+    NULL,                /* tp_as_number   */
+    NULL,                /* tp_as_sequence */
+    NULL,                /* tp_as_mapping  */
+    NULL                 /* tp_hash        */
+};
+
+
+
+/*
+ * *********************** MODULE WRAPPER ***********************
+ */
+
+static void handle_child_death(int dummy)
+{
+    while ( waitpid(-1, NULL, WNOHANG) > 0 )
+        continue;
+}
+
+static PyObject *xu_autoreap(PyObject *self, PyObject *args)
+{
+    struct sigaction sa;
+
+    if ( !PyArg_ParseTuple(args, "") )
+        return NULL;
+
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_handler = handle_child_death;
+    sigemptyset(&sa.sa_mask);
+    sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+    (void)sigaction(SIGCHLD, &sa, NULL);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyMethodDef xu_methods[] = {
+    { "notifier", xu_notifier_new, METH_VARARGS, 
+      "Create a new notifier." },
+    { "message", xu_message_new, METH_VARARGS, 
+      "Create a new communications message." },
+    { "port", xu_port_new, METH_VARARGS, 
+      "Create a new communications port." },
+    { "buffer", xu_buffer_new, METH_VARARGS, 
+      "Create a new ring buffer." },
+    { "autoreap", xu_autoreap, METH_VARARGS,
+      "Ensure that zombie children are automatically reaped by the OS." },
+    { NULL, NULL, 0, NULL }
+};
+
+PyMODINIT_FUNC initutils(void)
+{
+    PyObject *m, *d;
+
+    m = Py_InitModule("xend.utils", xu_methods);
+
+    d = PyModule_GetDict(m);
+    port_error = PyErr_NewException("xend.utils.PortError", NULL, NULL);
+    PyDict_SetItemString(d, "PortError", port_error);
+}
index 9bc967785fced6fbb56a4040046272aa3b9e06f0..3dc84d61fa6e30abeccb9013ca63f984afe4b82d 100644 (file)
@@ -1,11 +1,17 @@
 
 from distutils.core import setup, Extension
 
-module = Extension("xend_utils",
-                   include_dirs         = ["../xc/lib",
-                                           "../../xenolinux-sparse/include"],
-                   library_dirs         = ["../xc/lib"],
-                   libraries            = ["xc"],
-                   sources              = ["xend_utils.c"])
+utils = Extension("utils",
+                  include_dirs         = ["../xc/lib",
+                                          "../../xenolinux-sparse/include"],
+                  library_dirs         = ["../xc/lib"],
+                  libraries            = ["xc"],
+                  sources              = ["lib/utils.c"])
 
-setup(name = "xend_utils", version = "1.0", ext_modules = [module])
+setup(name = "xend",
+      version = "1.0",
+      packages = ["xend"],
+      package_dir = { "xend" : "lib" },
+      ext_package = "xend",
+      ext_modules = [ utils ]
+      )
diff --git a/tools/xend/xend.py b/tools/xend/xend.py
deleted file mode 100755 (executable)
index 313dc06..0000000
+++ /dev/null
@@ -1,454 +0,0 @@
-#!/usr/bin/env python
-
-
-###########################################################
-## xend.py -- Xen controller daemon
-## Copyright (c) 2004, K A Fraser (University of Cambridge)
-###########################################################
-
-
-import errno, re, os, pwd, select, signal, socket, struct, sys, tempfile, time
-import xend_utils, Xc
-
-
-
-# The following parameters could be placed in a configuration file.
-PID  = '/var/run/xend.pid'
-LOG  = '/var/log/xend.log'
-USER = 'root'
-CONTROL_DIR  = '/var/run/xend'
-UNIX_SOCK    = 'management_sock' # relative to CONTROL_DIR
-
-
-
-##
-## console_interface:
-##  Each control interface owns an instance of this class, which manages
-##  the current state of the console interface. Normally a console interface
-##  will be one of two state:
-##   LISTENING: listening for a connection on TCP port 'self.port'
-##   CONNECTED: sending/receiving console data on TCP port 'self.port'
-##
-##  A dictionary of all active interfaces, indexed by TCP socket descriptor,
-##  is accessible as 'console_interface.interface_list'.
-##
-##  NB. When a class instance is to be destroyed you *must* call the 'close'
-##  method. Otherwise a stale reference will eb left in the interface list.
-##
-class console_interface:
-
-    # The various states that a console interface may be in.
-    CLOSED    = 0 # No console activity
-    LISTENING = 1 # Listening on port 'self.port'. Socket object 'self.sock'.
-    CONNECTED = 2 # Active connection on 'self.port'. Socket obj 'self.sock'.
-
-
-    # Dictionary of all active (non-closed) console interfaces.
-    interface_list = {}
-
-
-    # NB. 'key' is an opaque value that has no meaning in this class.
-    def __init__(self, port, key):
-        self.status = console_interface.CLOSED
-        self.port   = port
-        self.key    = key
-
-
-    # Is this interface closed (inactive)?
-    def closed(self):
-        return self.status == console_interface.CLOSED
-
-
-    # Is this interface listening?
-    def listening(self):
-        return self.status == console_interface.LISTENING
-
-
-    # Is this interface active and connected?
-    def connected(self):
-        return self.status == console_interface.CONNECTED
-
-
-    # Close the interface, if it is not closed already.
-    def close(self):
-        if not self.closed():
-            del console_interface.interface_list[self.sock.fileno()]
-            self.sock.close()
-            del self.sock
-            self.status = console_interface.CLOSED
-
-
-    # Move the interface into the 'listening' state. Opens a new listening
-    # socket and updates 'interface_list'.
-    def listen(self):
-        # Close old socket (if any), and create a fresh one.
-        self.close()
-        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
-
-        try:
-            # Turn the new socket into a non-blocking listener.
-            self.sock.setblocking(False)
-            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
-                                 struct.pack('ii', 0, 0))
-            self.sock.bind(('', self.port))
-            self.sock.listen(1)
-
-            # Announce the new status of thsi interface.
-            self.status = console_interface.LISTENING
-            console_interface.interface_list[self.sock.fileno()] = self
-
-        except:
-            # In case of trouble ensure we get rid of dangling socket reference
-            self.sock.close()
-            del self.sock
-            raise
-
-
-    # Move a listening interface into the 'connected' state.
-    def connect(self):
-        # Pick up a new connection, if one is available.
-        try:
-            (sock, addr) = self.sock.accept()
-        except:
-            return 0
-        sock.setblocking(False)
-        sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
-                        struct.pack('ii', 0, 0))
-
-        # Close the listening socket.
-        self.sock.close()
-
-        # Publish the new socket and the new interface state.
-        self.sock = sock
-        self.status = console_interface.CONNECTED
-        console_interface.interface_list[self.sock.fileno()] = self
-        return 1
-
-
-
-##
-## new_control_interface:
-##  Create a new control interface with the specified domain 'dom'.
-##  The console port may also be specified; otehrwise a suitable port is
-##  automatically allocated.
-##
-def new_control_interface(dom, console_port=-1):
-    # Allocate an event channel. Clear pending notifications.
-    port = xend_utils.port(dom)
-    notifier.clear(port.local_port, notifier.NORMAL)
-    notifier.clear(port.local_port, notifier.DISCONNECT)
-    
-    # If necessary, compute a suitable TCP port for console I/O.
-    if console_port < 0:
-        console_port = 9600 + port.local_port
-
-    # Create a listenign console interface.
-    con_if = console_interface(console_port, port.local_port)
-    con_if.listen()
-
-    # Add control state to the master list.
-    control_list[port.local_port] = \
-      (port, xend_utils.buffer(), xend_utils.buffer(), con_if)
-
-    # Construct the successful response to be returned to the requester.
-    response = { 'success': True }
-    response['local_port']   = port.local_port
-    response['remote_port']  = port.remote_port
-    response['console_port'] = console_port
-    return response
-
-
-        
-def daemon_loop():
-    global control_list, notifier
-
-    xc = Xc.new()
-    control_list = {}
-
-    # Ignore writes to disconnected sockets. We clean up differently.
-    signal.signal(signal.SIGPIPE, signal.SIG_IGN)
-
-    # Construct the management interface. This is a UNIX domain socket via
-    # which we receive 'request' datagrams. Each request is a string that
-    # can be eval'ed as a Python statement. Responses can be remotely eval'ed
-    # by the requester to create a Python dictionary of result values.
-    management_interface = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
-    if os.path.exists(CONTROL_DIR+'/'+UNIX_SOCK):
-        os.unlink(CONTROL_DIR+'/'+UNIX_SOCK)
-    management_interface.setblocking(False)
-    management_interface.bind(CONTROL_DIR+'/'+UNIX_SOCK)
-
-    notifier = xend_utils.notifier()
-
-    ##
-    ## MAIN LOOP
-    ## 
-    while 1:
-
-        # Construct a poll set. We wait on:
-        #  1. Requests on the management interface.
-        #  2. Incoming event-channel notifications.
-        # Furthermore, for each active control interface:
-        #  3. Incoming console data.
-        #  4. Space for outgoing console data (if there is data to send).
-        waitset = select.poll()
-        waitset.register(management_interface, select.POLLIN)
-        waitset.register(notifier, select.POLLIN)
-        for idx, (port, rbuf, wbuf, con_if) in control_list.items():
-            if not con_if.closed():
-                pflags = select.POLLIN
-                if not rbuf.empty() and con_if.connected():
-                    pflags = select.POLLIN | select.POLLOUT
-                waitset.register(con_if.sock.fileno(), pflags)
-
-        # Wait for something to do...
-        fdset = waitset.poll()
-        
-        # Look for messages on the management interface.
-        # These should consist of executable Python statements that call
-        # well-known management functions (e.g., new_control_interface(dom=9)).
-        try:
-            data, addr = management_interface.recvfrom(2048)
-        except socket.error, error:
-            if error[0] != errno.EAGAIN:
-                raise
-        else:
-            if addr:
-                # Evaluate the request in an exception-trapping sandbox.
-                try:
-                    print "Mgmt_req[%s]: %s" % (addr, data)
-                    response = str(eval(data))
-
-                except:
-                    # Catch all exceptions and turn into an error response:
-                    #  status:          False
-                    #  error_type:      'exception'
-                    #  exception_type:  name of exception type.
-                    #  exception value: textual exception value.
-                    exc_type, exc_val = sys.exc_info()[:2]
-                    response = { 'success': False }
-                    response['error_type'] = 'exception'
-                    response['exception_type'] = str(exc_type)
-                    response['exception_value'] = str(exc_val)
-                    response = str(response)
-
-                # Try to send a response to the requester.
-                try:
-                    print "Mgmt_rsp[%s]: %s" % (addr, response)
-                    management_interface.sendto(response, addr)
-                except socket.error, error:
-                    pass
-                
-        # Do work for every console interface that hit in the poll set.
-        for (fd, events) in fdset:
-            if not console_interface.interface_list.has_key(fd):
-                continue
-            con_if = console_interface.interface_list[fd]
-
-            # If the interface is listening, check for pending connections.
-            if con_if.listening():
-                con_if.connect()
-
-            # All done if the interface is not connected.
-            if not con_if.connected():
-                continue
-            (port, rbuf, wbuf, con_if) = control_list[con_if.key]
-
-            # Send as much pending data as possible via the socket.
-            while not rbuf.empty():
-                try:
-                    bytes = con_if.sock.send(rbuf.peek())
-                    if bytes > 0:
-                        rbuf.discard(bytes)
-                except socket.error, error:
-                    pass
-
-            # Read as much data as is available. Don't worry about
-            # overflowing our buffer: it's more important to read the
-            # incoming data stream and detect errors or closure of the
-            # remote end in a timely manner.
-            try:
-                while 1:
-                    data = con_if.sock.recv(2048)
-                    # Return of zero means the remote end has disconnected.
-                    # We therefore return the console interface to listening.
-                    if not data:
-                        con_if.listen()
-                        break
-                    wbuf.write(data)
-            except socket.error, error:
-                # Assume that most errors mean that the connection is dead.
-                # In such cases we return the interface to 'listening' state.
-                if error[0] != errno.EAGAIN:
-                    print "Better return to listening"
-                    con_if.listen()
-                    print "New status: " + str(con_if.status)
-
-            # We may now have pending data to send via the relevant
-            # inter-domain control interface. If so then we send all we can
-            # and notify the remote end.
-            work_done = False
-            while not wbuf.empty() and port.space_to_write_request():
-                msg = xend_utils.message(0, 0, 0)
-                msg.append_payload(wbuf.read(msg.MAX_PAYLOAD))
-                port.write_request(msg)
-                work_done = True
-            if work_done:
-                port.notify()
-
-        # Process control-interface notifications from other guest OSes.
-        while 1:            
-            # Grab a notification, if there is one.
-            notification = notifier.read()
-            if not notification:
-                break
-            (idx, type) = notification
-
-            # If we pick up a disconnect notification then we do any necessary
-            # cleanup, even if the event channel doesn't belong to us.
-            # This is intended to prevent the event-channel port space from
-            # getting clogged with stale connections.
-            if type == notifier.DISCONNECT:
-                ret = xc.evtchn_status(idx)
-                if ret['status'] != 'connected':
-                    notifier.clear(idx, notifier.NORMAL)
-                    notifier.clear(idx, notifier.DISCONNECT)
-                    if control_list.has_key(idx):
-                        (port, rbuf, wbuf, con_if) =  control_list[idx]
-                        con_if.close()
-                        del control_list[idx], port, rbuf, wbuf, con_if
-                    elif ret['status'] == 'disconnected':
-                        # There's noone to do the closure for us...
-                        xc.evtchn_close(idx)
-
-            # A standard notification: probably means there are messages to
-            # read or that there is space to write messages.
-            elif type == notifier.NORMAL and control_list.has_key(idx):
-                (port, rbuf, wbuf, con_if) = control_list[idx]
-                work_done = False
-
-                # We clear the notification before doing any work, to avoid
-                # races.
-                notifier.clear(idx, notifier.NORMAL)
-
-                # Read incoming requests. Currently assume that request
-                # message always containb console data.
-                while port.request_to_read():
-                    msg = port.read_request()
-                    rbuf.write(msg.get_payload())
-                    port.write_response(msg)
-                    work_done = True
-
-                # Incoming responses are currently thrown on the floor.
-                while port.response_to_read():
-                    msg = port.read_response()
-                    work_done = True
-
-                # Send as much pending console data as there is room for.
-                while not wbuf.empty() and port.space_to_write_request():
-                    msg = xend_utils.message(0, 0, 0)
-                    msg.append_payload(wbuf.read(msg.MAX_PAYLOAD))
-                    port.write_request(msg)
-                    work_done = True
-
-                # Finally, notify the remote end of any work that we did.
-                if work_done:
-                    port.notify()
-
-
-
-def cleanup_daemon(kill=False):
-    # No cleanup to do if the PID file is empty.
-    if not os.path.isfile(PID) or not os.path.getsize(PID):
-        return 0
-    # Read the PID of the previous invocation and search active process list.
-    pid = open(PID, 'r').read()
-    lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines()
-    for line in lines:
-        if re.search('^ *' + pid + '.+xend', line):
-            if not kill:
-                print "Daemon is already running (PID %d)" % int(pid)
-                return 1
-            # Old daemon is still active: terminate it.
-            os.kill(int(pid), 1)
-    # Delete the, now stale, PID file.
-    os.remove(PID)
-    return 0
-
-
-
-def start_daemon():
-    if cleanup_daemon(kill=False):
-        return 1
-
-    if not os.path.exists(CONTROL_DIR):
-        os.mkdir(CONTROL_DIR)
-
-    # Open log file. Truncate it if non-empty, and request line buffering.
-    if os.path.isfile(LOG):
-        os.rename(LOG, LOG+'.old')
-    logfile = open(LOG, 'w+', 1)
-
-    # Detach from TTY.
-    os.setsid()
-
-    # Set the UID.
-    try:
-        os.setuid(pwd.getpwnam(USER)[2])
-    except KeyError, error:
-        print "Error: no such user '%s'" % USER
-        return 1
-
-    # Ensure that zombie children are automatically reaped.
-    xend_utils.autoreap()
-
-    # Fork -- parent writes the PID file and exits.
-    pid = os.fork()
-    if pid:
-        pidfile = open(PID, 'w')
-        pidfile.write(str(pid))
-        pidfile.close()
-        return 0
-
-    # Close down standard file handles
-    try:
-        os.close(0) # stdin
-        os.close(1) # stdout
-        os.close(2) # stderr
-    except:
-        pass
-
-    # Redirect output to log file, then enter the main loop.
-    sys.stdout = sys.stderr = logfile
-    daemon_loop()
-    return 0
-
-
-
-def stop_daemon():
-    return cleanup_daemon(kill=True)
-
-
-
-def main():
-    xend_utils.autoreap()
-    if not sys.argv[1:]:
-        print 'usage: %s {start|stop|restart}' % sys.argv[0]
-    elif os.fork():
-        pid, status = os.wait()
-        return status >> 8
-    elif sys.argv[1] == 'start':
-        return start_daemon()
-    elif sys.argv[1] == 'stop':
-        return stop_daemon()
-    elif sys.argv[1] == 'restart':
-        return stop_daemon() or start_daemon()
-    else:
-        print 'not an option:', sys.argv[1]
-    return 1
-
-
-
-if __name__ == '__main__':
-    sys.exit(main())
diff --git a/tools/xend/xend_utils.c b/tools/xend/xend_utils.c
deleted file mode 100644 (file)
index 81b101e..0000000
+++ /dev/null
@@ -1,1006 +0,0 @@
-/******************************************************************************
- * xend_utils.c
- * 
- * Copyright (c) 2004, K A Fraser
- */
-
-#include <Python.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/mman.h>
-#include <sys/poll.h>
-#include <netinet/in.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <signal.h>
-#include <xc.h>
-#include <asm-xeno/control_if.h>
-
-/* NB. The following should be kept in sync with the kernel's evtchn driver. */
-#define EVTCHN_DEV_NAME  "/dev/xen/evtchn"
-#define EVTCHN_DEV_MAJOR 10
-#define EVTCHN_DEV_MINOR 200
-#define PORT_NORMAL     0x0000   /* A standard event notification.      */ 
-#define PORT_DISCONNECT 0x8000   /* A port-disconnect notification.     */
-#define PORTIDX_MASK    0x7fff   /* Strip subtype to obtain port index. */
-#define EVTCHN_RESET _IO('E', 1) /* Clear notification buffer. Clear errors. */
-
-/* Size of a machine page frame. */
-#define PAGE_SIZE 4096
-
-
-/*
- * *********************** NOTIFIER ***********************
- */
-
-typedef struct {
-    PyObject_HEAD;
-    int evtchn_fd;
-} xu_notifier_object;
-
-static PyObject *xu_notifier_read(PyObject *self, PyObject *args)
-{
-    xu_notifier_object *xun = (xu_notifier_object *)self;
-    u16 v;
-    int bytes;
-
-    if ( !PyArg_ParseTuple(args, "") )
-        return NULL;
-    
-    while ( (bytes = read(xun->evtchn_fd, &v, sizeof(v))) == -1 )
-    {
-        if ( errno == EINTR )
-            continue;
-        if ( errno == EAGAIN )
-            goto none;
-        return PyErr_SetFromErrno(PyExc_IOError);
-    }
-    
-    if ( bytes == sizeof(v) )
-        return Py_BuildValue("(i,i)", v&PORTIDX_MASK, v&~PORTIDX_MASK);
-
- none:
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-static PyObject *xu_notifier_clear(PyObject *self, PyObject *args)
-{
-    xu_notifier_object *xun = (xu_notifier_object *)self;
-    u16 v;
-    int idx, type;
-
-    if ( !PyArg_ParseTuple(args, "ii", &idx, &type) )
-        return NULL;
-    
-    v = (u16)idx | (u16)type;
-
-    (void)write(xun->evtchn_fd, &v, sizeof(v));
-
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-static PyObject *xu_notifier_fileno(PyObject *self, PyObject *args)
-{
-    xu_notifier_object *xun = (xu_notifier_object *)self;
-    return PyInt_FromLong(xun->evtchn_fd);
-}
-
-static PyMethodDef xu_notifier_methods[] = {
-    { "read",
-      (PyCFunction)xu_notifier_read,
-      METH_VARARGS,
-      "Read a (@port, @type) pair.\n" },
-
-    { "clear", 
-      (PyCFunction)xu_notifier_clear,
-      METH_VARARGS,
-      "Clear a (@port, @type) pair.\n" },
-
-    { "fileno", 
-      (PyCFunction)xu_notifier_fileno,
-      METH_VARARGS,
-      "Return the file descriptor for the notification channel.\n" },
-
-    { NULL, NULL, 0, NULL }
-};
-
-staticforward PyTypeObject xu_notifier_type;
-
-static PyObject *xu_notifier_new(PyObject *self, PyObject *args)
-{
-    xu_notifier_object *xun;
-
-    if ( !PyArg_ParseTuple(args, "") )
-        return NULL;
-
-    xun = PyObject_New(xu_notifier_object, &xu_notifier_type);
-
-    xun->evtchn_fd = open(EVTCHN_DEV_NAME, O_NONBLOCK|O_RDWR);
-    if ( xun->evtchn_fd == -1 )
-    {
-        PyObject_Del((PyObject *)xun);
-        return PyErr_SetFromErrno(PyExc_IOError);
-    }
-
-    return (PyObject *)xun;
-}
-
-static PyObject *xu_notifier_getattr(PyObject *obj, char *name)
-{
-    if ( strcmp(name, "DISCONNECT") == 0 )
-        return PyInt_FromLong(PORT_DISCONNECT);
-    if ( strcmp(name, "NORMAL") == 0 )
-        return PyInt_FromLong(PORT_NORMAL);
-    return Py_FindMethod(xu_notifier_methods, obj, name);
-}
-
-static void xu_notifier_dealloc(PyObject *self)
-{
-    xu_notifier_object *xun = (xu_notifier_object *)self;
-    (void)close(xun->evtchn_fd);
-    PyObject_Del(self);
-}
-
-static PyTypeObject xu_notifier_type = {
-    PyObject_HEAD_INIT(&PyType_Type)
-    0,
-    "notifier",
-    sizeof(xu_notifier_object),
-    0,
-    xu_notifier_dealloc, /* tp_dealloc     */
-    NULL,                /* tp_print       */
-    xu_notifier_getattr, /* tp_getattr     */
-    NULL,                /* tp_setattr     */
-    NULL,                /* tp_compare     */
-    NULL,                /* tp_repr        */
-    NULL,                /* tp_as_number   */
-    NULL,                /* tp_as_sequence */
-    NULL,                /* tp_as_mapping  */
-    NULL                 /* tp_hash        */
-};
-
-
-
-/*
- * *********************** MESSAGE ***********************
- */
-
-typedef struct {
-    PyObject_HEAD;
-    control_msg_t msg;
-} xu_message_object;
-
-static PyObject *xu_message_append_payload(PyObject *self, PyObject *args)
-{
-    xu_message_object *xum = (xu_message_object *)self;
-    char *str;
-    int len;
-
-    if ( !PyArg_ParseTuple(args, "s#", &str, &len) )
-        return NULL;
-
-    if ( (len + xum->msg.length) > sizeof(xum->msg.msg) )
-    {
-        PyErr_SetString(PyExc_RuntimeError, "out of space in control message");
-        return NULL;
-    }
-
-    memcpy(&xum->msg.msg[xum->msg.length], str, len);
-    xum->msg.length += len;
-
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-static PyObject *xu_message_get_payload(PyObject *self, PyObject *args)
-{
-    xu_message_object *xum = (xu_message_object *)self;
-
-    if ( !PyArg_ParseTuple(args, "") )
-        return NULL;
-
-    return PyString_FromStringAndSize(xum->msg.msg, xum->msg.length);
-}
-
-static PyObject *xu_message_set_header(PyObject *self, 
-                                       PyObject *args, 
-                                       PyObject *kwds)
-{
-    xu_message_object *xum = (xu_message_object *)self;
-    int type = -1, subtype = -1, id = -1;
-
-    static char *kwd_list[] = { "type", "subtype", "id", NULL };
-
-    if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|iii", kwd_list,
-                                      &type, &subtype, &id) )
-        return NULL;
-
-    if ( type != -1 )
-        xum->msg.type = (u8)type;
-    if ( subtype != -1 )
-        xum->msg.subtype = (u8)subtype;
-    if ( id != -1 )
-        xum->msg.id = (u8)id;
-
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-static PyObject *xu_message_get_header(PyObject *self, PyObject *args)
-{
-    xu_message_object *xum = (xu_message_object *)self;
-
-    if ( !PyArg_ParseTuple(args, "") )
-        return NULL;
-
-    return Py_BuildValue("{s:i,s:i,s:i}",
-                         "type",    xum->msg.type,
-                         "subtype", xum->msg.subtype,
-                         "id",      xum->msg.id);
-}
-
-static PyMethodDef xu_message_methods[] = {
-    { "append_payload", 
-      (PyCFunction)xu_message_append_payload,
-      METH_VARARGS,
-      "Append @str to the message payload.\n" },
-
-    { "get_payload",
-      (PyCFunction)xu_message_get_payload,
-      METH_VARARGS,
-      "Return the message payload in string form.\n" },
-
-    { "set_header",
-      (PyCFunction)xu_message_set_header,
-      METH_VARARGS | METH_KEYWORDS,
-      "Accepts keywords @type, @subtype, and @id.\n" },
-
-    { "get_header",
-      (PyCFunction)xu_message_get_header,
-      METH_VARARGS,
-      "Returns a dictionary of values for @type, @subtype, and @id.\n" },
-
-    { NULL, NULL, 0, NULL }
-};
-
-staticforward PyTypeObject xu_message_type;
-
-static PyObject *xu_message_new(PyObject *self, PyObject *args)
-{
-    xu_message_object *xum;
-    int type, subtype, id;
-
-    if ( !PyArg_ParseTuple(args, "iii", &type, &subtype, &id) )
-        return NULL;
-
-    xum = PyObject_New(xu_message_object, &xu_message_type);
-
-    xum->msg.type    = type;
-    xum->msg.subtype = subtype;
-    xum->msg.id      = id;
-    xum->msg.length  = 0;
-
-    return (PyObject *)xum;
-}
-
-static PyObject *xu_message_getattr(PyObject *obj, char *name)
-{
-    xu_message_object *xum;
-    if ( strcmp(name, "MAX_PAYLOAD") == 0 )
-        return PyInt_FromLong(sizeof(xum->msg.msg));
-    return Py_FindMethod(xu_message_methods, obj, name);
-}
-
-static void xu_message_dealloc(PyObject *self)
-{
-    PyObject_Del(self);
-}
-
-static PyTypeObject xu_message_type = {
-    PyObject_HEAD_INIT(&PyType_Type)
-    0,
-    "message",
-    sizeof(xu_message_object),
-    0,
-    xu_message_dealloc,   /* tp_dealloc     */
-    NULL,                /* tp_print       */
-    xu_message_getattr,   /* tp_getattr     */
-    NULL,                /* tp_setattr     */
-    NULL,                /* tp_compare     */
-    NULL,                /* tp_repr        */
-    NULL,                /* tp_as_number   */
-    NULL,                /* tp_as_sequence */
-    NULL,                /* tp_as_mapping  */
-    NULL                 /* tp_hash        */
-};
-
-
-
-/*
- * *********************** PORT ***********************
- */
-
-static control_if_t *map_control_interface(int fd, unsigned long pfn)
-{
-    char *vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE,
-                       MAP_SHARED, fd, pfn * PAGE_SIZE);
-    if ( vaddr == MAP_FAILED )
-        return NULL;
-    return (control_if_t *)(vaddr + 2048);
-}
-static void unmap_control_interface(int fd, control_if_t *c)
-{
-    char *vaddr = (char *)c - 2048;
-    (void)munmap(vaddr, PAGE_SIZE);
-}
-
-typedef struct {
-    PyObject_HEAD;
-    int mem_fd;
-    int xc_handle;
-    u64 remote_dom;
-    int local_port, remote_port;
-    control_if_t    *interface;
-    CONTROL_RING_IDX tx_req_cons, tx_resp_prod;
-    CONTROL_RING_IDX rx_req_prod, rx_resp_cons;
-} xu_port_object;
-
-static PyObject *port_error;
-
-static PyObject *xu_port_notify(PyObject *self, PyObject *args)
-{
-    xu_port_object *xup = (xu_port_object *)self;
-
-    if ( !PyArg_ParseTuple(args, "") )
-        return NULL;
-
-    (void)xc_evtchn_send(xup->xc_handle, xup->local_port);
-
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-static PyObject *xu_port_read_request(PyObject *self, PyObject *args)
-{
-    xu_port_object    *xup = (xu_port_object *)self;
-    xu_message_object *xum;
-    CONTROL_RING_IDX   c = xup->tx_req_cons;
-    control_if_t      *cif = xup->interface;
-    control_msg_t     *cmsg;
-
-    if ( !PyArg_ParseTuple(args, "") )
-        return NULL;
-
-    if ( (c == cif->tx_req_prod) || 
-         ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) )
-    {
-        PyErr_SetString(port_error, "no request to read");
-        return NULL;
-    }
-
-    cmsg = &cif->tx_ring[MASK_CONTROL_IDX(c)];
-    xum = PyObject_New(xu_message_object, &xu_message_type);
-    memcpy(&xum->msg, cmsg, sizeof(*cmsg));
-    if ( xum->msg.length > sizeof(xum->msg.msg) )
-        xum->msg.length = sizeof(xum->msg.msg);
-    xup->tx_req_cons++;
-    return (PyObject *)xum;
-}
-
-static PyObject *xu_port_write_request(PyObject *self, PyObject *args)
-{
-    xu_port_object    *xup = (xu_port_object *)self;
-    xu_message_object *xum;
-    CONTROL_RING_IDX   p = xup->rx_req_prod;
-    control_if_t      *cif = xup->interface;
-    control_msg_t     *cmsg;
-
-    if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) )
-        return NULL;
-
-    if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) )
-    {
-        PyErr_SetString(PyExc_TypeError, "expected a xend_utils.message");
-        return NULL;        
-    }
-
-    if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) )
-    {
-        PyErr_SetString(port_error, "no space to write request");
-        return NULL;
-    }
-
-    cmsg = &cif->rx_ring[MASK_CONTROL_IDX(p)];
-    memcpy(cmsg, &xum->msg, sizeof(*cmsg));
-
-    xup->rx_req_prod = cif->rx_req_prod = p + 1;
-
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-static PyObject *xu_port_read_response(PyObject *self, PyObject *args)
-{
-    xu_port_object    *xup = (xu_port_object *)self;
-    xu_message_object *xum;
-    CONTROL_RING_IDX   c = xup->rx_resp_cons;
-    control_if_t      *cif = xup->interface;
-    control_msg_t     *cmsg;
-
-    if ( !PyArg_ParseTuple(args, "") )
-        return NULL;
-
-    if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) )
-    {
-        PyErr_SetString(port_error, "no response to read");
-        return NULL;
-    }
-
-    cmsg = &cif->rx_ring[MASK_CONTROL_IDX(c)];
-    xum = PyObject_New(xu_message_object, &xu_message_type);
-    memcpy(&xum->msg, cmsg, sizeof(*cmsg));
-    if ( xum->msg.length > sizeof(xum->msg.msg) )
-        xum->msg.length = sizeof(xum->msg.msg);
-    xup->rx_resp_cons++;
-    return (PyObject *)xum;
-}
-
-static PyObject *xu_port_write_response(PyObject *self, PyObject *args)
-{
-    xu_port_object    *xup = (xu_port_object *)self;
-    xu_message_object *xum;
-    CONTROL_RING_IDX   p = xup->tx_resp_prod;
-    control_if_t      *cif = xup->interface;
-    control_msg_t     *cmsg;
-
-    if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) )
-        return NULL;
-
-    if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) )
-    {
-        PyErr_SetString(PyExc_TypeError, "expected a xend_utils.message");
-        return NULL;        
-    }
-
-    if ( p == xup->tx_req_cons )
-    {
-        PyErr_SetString(port_error, "no space to write response");
-        return NULL;
-    }
-
-    cmsg = &cif->tx_ring[MASK_CONTROL_IDX(p)];
-    memcpy(cmsg, &xum->msg, sizeof(*cmsg));
-
-    xup->tx_resp_prod = cif->tx_resp_prod = p + 1;
-
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-static PyObject *xu_port_request_to_read(PyObject *self, PyObject *args)
-{
-    xu_port_object    *xup = (xu_port_object *)self;
-    CONTROL_RING_IDX   c = xup->tx_req_cons;
-    control_if_t      *cif = xup->interface;
-
-    if ( !PyArg_ParseTuple(args, "") )
-        return NULL;
-
-    if ( (c == cif->tx_req_prod) || 
-         ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) )
-        return PyInt_FromLong(0);
-
-    return PyInt_FromLong(1);
-}
-
-static PyObject *xu_port_space_to_write_request(PyObject *self, PyObject *args)
-{
-    xu_port_object    *xup = (xu_port_object *)self;
-    CONTROL_RING_IDX   p = xup->rx_req_prod;
-
-    if ( !PyArg_ParseTuple(args, "") )
-        return NULL;
-
-    if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) )
-        return PyInt_FromLong(0);
-
-    return PyInt_FromLong(1);
-}
-
-static PyObject *xu_port_response_to_read(PyObject *self, PyObject *args)
-{
-    xu_port_object    *xup = (xu_port_object *)self;
-    CONTROL_RING_IDX   c = xup->rx_resp_cons;
-    control_if_t      *cif = xup->interface;
-
-    if ( !PyArg_ParseTuple(args, "") )
-        return NULL;
-
-    if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) )
-        return PyInt_FromLong(0);
-
-    return PyInt_FromLong(1);
-}
-
-static PyObject *xu_port_space_to_write_response(
-    PyObject *self, PyObject *args)
-{
-    xu_port_object    *xup = (xu_port_object *)self;
-    CONTROL_RING_IDX   p = xup->tx_resp_prod;
-
-    if ( !PyArg_ParseTuple(args, "") )
-        return NULL;
-
-    if ( p == xup->tx_req_cons )
-        return PyInt_FromLong(0);
-
-    return PyInt_FromLong(1);
-}
-
-static PyMethodDef xu_port_methods[] = {
-    { "notify",
-      (PyCFunction)xu_port_notify,
-      METH_VARARGS,
-      "Send a notification to the remote end.\n" },
-
-    { "read_request",
-      (PyCFunction)xu_port_read_request,
-      METH_VARARGS,
-      "Read a request message from the control interface.\n" },
-
-    { "write_request",
-      (PyCFunction)xu_port_write_request,
-      METH_VARARGS,
-      "Write a request message to the control interface.\n" },
-
-    { "read_response",
-      (PyCFunction)xu_port_read_response,
-      METH_VARARGS,
-      "Read a response message from the control interface.\n" },
-
-    { "write_response",
-      (PyCFunction)xu_port_write_response,
-      METH_VARARGS,
-      "Write a response message to the control interface.\n" },
-
-    { "request_to_read",
-      (PyCFunction)xu_port_request_to_read,
-      METH_VARARGS,
-      "Returns TRUE if there is a request message to read.\n" },
-
-    { "space_to_write_request",
-      (PyCFunction)xu_port_space_to_write_request,
-      METH_VARARGS,
-      "Returns TRUE if there is space to write a request message.\n" },
-
-    { "response_to_read",
-      (PyCFunction)xu_port_response_to_read,
-      METH_VARARGS,
-      "Returns TRUE if there is a response message to read.\n" },
-
-    { "space_to_write_response",
-      (PyCFunction)xu_port_space_to_write_response,
-      METH_VARARGS,
-      "Returns TRUE if there is space to write a response message.\n" },
-
-    { NULL, NULL, 0, NULL }
-};
-
-staticforward PyTypeObject xu_port_type;
-
-static PyObject *xu_port_new(PyObject *self, PyObject *args)
-{
-    xu_port_object *xup;
-    u64 dom;
-    int port1, port2;
-    xc_dominfo_t info;
-
-    if ( !PyArg_ParseTuple(args, "L", &dom) )
-        return NULL;
-
-    xup = PyObject_New(xu_port_object, &xu_port_type);
-
-    if ( (xup->mem_fd = open("/dev/mem", O_RDWR)) == -1 )
-    {
-        PyErr_SetString(port_error, "Could not open '/dev/mem'");
-        goto fail1;
-    }
-
-    if ( (xup->xc_handle = xc_interface_open()) == -1 )
-    {
-        PyErr_SetString(port_error, "Could not open Xen control interface");
-        goto fail2;
-    }
-
-    if ( xc_evtchn_open(xup->xc_handle, DOMID_SELF, dom, &port1, &port2) != 0 )
-    {
-        PyErr_SetString(port_error, "Could not open channel to domain");
-        goto fail3;
-    }
-
-    if ( (xc_domain_getinfo(xup->xc_handle, dom, 1, &info) != 1) ||
-         (info.domid != dom) )
-    {
-        PyErr_SetString(port_error, "Failed to obtain domain status");
-        goto fail4;
-    }
-
-    xup->interface = 
-        map_control_interface(xup->mem_fd, info.shared_info_frame);
-    if ( xup->interface == NULL )
-    {
-        PyErr_SetString(port_error, "Failed to map domain control interface");
-        goto fail4;
-    }
-
-    xup->tx_req_cons  = 0;
-    xup->tx_resp_prod = 0;
-    xup->rx_req_prod  = 0;
-    xup->rx_resp_cons = 0;
-    xup->remote_dom   = dom;
-    xup->local_port   = port1;
-    xup->remote_port  = port2;
-
-    return (PyObject *)xup;
-
-    
- fail4:
-    (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, port1);
- fail3:
-    (void)xc_interface_close(xup->xc_handle);
- fail2:
-    (void)close(xup->mem_fd);
- fail1:
-    PyObject_Del((PyObject *)xup);
-    return NULL;        
-}
-
-static PyObject *xu_port_getattr(PyObject *obj, char *name)
-{
-    xu_port_object *xup = (xu_port_object *)obj;
-    if ( strcmp(name, "local_port") == 0 )
-        return PyInt_FromLong(xup->local_port);
-    if ( strcmp(name, "remote_port") == 0 )
-        return PyInt_FromLong(xup->remote_port);
-    if ( strcmp(name, "remote_dom") == 0 )
-        return PyLong_FromUnsignedLongLong(xup->remote_dom);
-    return Py_FindMethod(xu_port_methods, obj, name);
-}
-
-static void xu_port_dealloc(PyObject *self)
-{
-    xu_port_object *xup = (xu_port_object *)self;
-    unmap_control_interface(xup->mem_fd, xup->interface);
-    (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, xup->local_port);
-    (void)xc_interface_close(xup->xc_handle);
-    (void)close(xup->mem_fd);
-    PyObject_Del(self);
-}
-
-static PyTypeObject xu_port_type = {
-    PyObject_HEAD_INIT(&PyType_Type)
-    0,
-    "port",
-    sizeof(xu_port_object),
-    0,
-    xu_port_dealloc,     /* tp_dealloc     */
-    NULL,                /* tp_print       */
-    xu_port_getattr,     /* tp_getattr     */
-    NULL,                /* tp_setattr     */
-    NULL,                /* tp_compare     */
-    NULL,                /* tp_repr        */
-    NULL,                /* tp_as_number   */
-    NULL,                /* tp_as_sequence */
-    NULL,                /* tp_as_mapping  */
-    NULL                 /* tp_hash        */
-};
-
-
-
-/*
- * *********************** BUFFER ***********************
- */
-
-#define BUFSZ 65536
-#define MASK_BUF_IDX(_i) ((_i)&(BUFSZ-1))
-typedef unsigned int BUF_IDX;
-
-typedef struct {
-    PyObject_HEAD;
-    char        *buf;
-    unsigned int prod, cons;
-} xu_buffer_object;
-
-static PyObject *__xu_buffer_peek(xu_buffer_object *xub, int max)
-{
-    PyObject *str1, *str2;
-    int len1, len2, c = MASK_BUF_IDX(xub->cons);
-
-    len1 = xub->prod - xub->cons;
-    if ( len1 > (BUFSZ - c) ) /* clip to ring wrap */
-        len1 = BUFSZ - c;
-    if ( len1 > max )         /* clip to specified maximum */
-        len1 = max;
-    if ( len1 < 0 )           /* sanity */
-        len1 = 0;
-
-    if ( (str1 = PyString_FromStringAndSize(&xub->buf[c], len1)) == NULL )
-        return NULL;
-
-    if ( (len1 < (xub->prod - xub->cons)) && (len1 < max) )
-    {
-        len2 = max - len1;
-        if ( len2 > MASK_BUF_IDX(xub->prod) )
-            len2 = MASK_BUF_IDX(xub->prod);
-        if ( len2 > 0 )
-        {
-            str2 = PyString_FromStringAndSize(&xub->buf[0], len2);
-            if ( str2 == NULL )
-                return NULL;
-            PyString_ConcatAndDel(&str1, str2);
-            if ( str1 == NULL )
-                return NULL;
-        }
-    }
-
-    return str1;
-}
-
-static PyObject *xu_buffer_peek(PyObject *self, PyObject *args)
-{
-    xu_buffer_object *xub = (xu_buffer_object *)self;
-    int max = 1024;
-
-    if ( !PyArg_ParseTuple(args, "|i", &max) )
-        return NULL;
-    
-    return __xu_buffer_peek(xub, max);
-}
-
-static PyObject *xu_buffer_read(PyObject *self, PyObject *args)
-{
-    xu_buffer_object *xub = (xu_buffer_object *)self;
-    PyObject *str;
-    int max = 1024;
-
-    if ( !PyArg_ParseTuple(args, "|i", &max) )
-        return NULL;
-
-    if ( (str = __xu_buffer_peek(xub, max)) != NULL )
-        xub->cons += PyString_Size(str);
-
-    return str;
-}
-
-static PyObject *xu_buffer_discard(PyObject *self, PyObject *args)
-{
-    xu_buffer_object *xub = (xu_buffer_object *)self;
-    int max, len;
-
-    if ( !PyArg_ParseTuple(args, "i", &max) )
-        return NULL;
-
-    len = xub->prod - xub->cons;
-    if ( len > max )
-        len = max;
-    if ( len < 0 )
-        len = 0;
-
-    xub->cons += len;
-
-    return PyInt_FromLong(len);
-}
-
-static PyObject *xu_buffer_write(PyObject *self, PyObject *args)
-{
-    xu_buffer_object *xub = (xu_buffer_object *)self;
-    char *str;
-    int len, len1, len2;
-
-    if ( !PyArg_ParseTuple(args, "s#", &str, &len) )
-        return NULL;
-
-    len1 = len;
-    if ( len1 > (BUFSZ - MASK_BUF_IDX(xub->prod)) )
-        len1 = BUFSZ - MASK_BUF_IDX(xub->prod);
-    if ( len1 > (BUFSZ - (xub->prod - xub->cons)) )
-        len1 = BUFSZ - (xub->prod - xub->cons);
-
-    if ( len1 == 0 )
-        return PyInt_FromLong(0);
-
-    memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[0], len1);
-    xub->prod += len1;
-
-    if ( len1 < len )
-    {
-        len2 = len - len1;
-        if ( len2 > (BUFSZ - MASK_BUF_IDX(xub->prod)) )
-            len2 = BUFSZ - MASK_BUF_IDX(xub->prod);
-        if ( len2 > (BUFSZ - (xub->prod - xub->cons)) )
-            len2 = BUFSZ - (xub->prod - xub->cons);
-        if ( len2 != 0 )
-        {
-            memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[len1], len2);
-            xub->prod += len2;
-            return PyInt_FromLong(len1 + len2);
-        }
-    }
-
-    return PyInt_FromLong(len1);
-}
-
-static PyObject *xu_buffer_empty(PyObject *self, PyObject *args)
-{
-    xu_buffer_object *xub = (xu_buffer_object *)self;
-
-    if ( !PyArg_ParseTuple(args, "") )
-        return NULL;
-
-    if ( xub->cons == xub->prod )
-        return PyInt_FromLong(1);
-
-    return PyInt_FromLong(0);
-}
-
-static PyObject *xu_buffer_full(PyObject *self, PyObject *args)
-{
-    xu_buffer_object *xub = (xu_buffer_object *)self;
-
-    if ( !PyArg_ParseTuple(args, "") )
-        return NULL;
-
-    if ( (xub->prod - xub->cons) == BUFSZ )
-        return PyInt_FromLong(1);
-
-    return PyInt_FromLong(0);
-}
-
-static PyMethodDef xu_buffer_methods[] = {
-    { "peek", 
-      (PyCFunction)xu_buffer_peek,
-      METH_VARARGS,
-      "Peek up to @max bytes from the buffer. Returns a string.\n" },
-
-    { "read", 
-      (PyCFunction)xu_buffer_read,
-      METH_VARARGS,
-      "Read up to @max bytes from the buffer. Returns a string.\n" },
-
-    { "discard", 
-      (PyCFunction)xu_buffer_discard,
-      METH_VARARGS,
-      "Discard up to @max bytes from the buffer. Returns number of bytes.\n" },
-
-    { "write", 
-      (PyCFunction)xu_buffer_write,
-      METH_VARARGS,
-      "Write @string into buffer. Return number of bytes written.\n" },
-
-    { "empty", 
-      (PyCFunction)xu_buffer_empty,
-      METH_VARARGS,
-      "Return TRUE if the buffer is empty.\n" },
-
-    { "full", 
-      (PyCFunction)xu_buffer_full,
-      METH_VARARGS,
-      "Return TRUE if the buffer is full.\n" },
-
-    { NULL, NULL, 0, NULL }
-};
-
-staticforward PyTypeObject xu_buffer_type;
-
-static PyObject *xu_buffer_new(PyObject *self, PyObject *args)
-{
-    xu_buffer_object *xub;
-
-    if ( !PyArg_ParseTuple(args, "") )
-        return NULL;
-
-    xub = PyObject_New(xu_buffer_object, &xu_buffer_type);
-
-    if ( (xub->buf = malloc(BUFSZ)) == NULL )
-    {
-        PyObject_Del((PyObject *)xub);
-        return NULL;
-    }
-
-    xub->prod = xub->cons = 0;
-
-    return (PyObject *)xub;
-}
-
-static PyObject *xu_buffer_getattr(PyObject *obj, char *name)
-{
-    return Py_FindMethod(xu_buffer_methods, obj, name);
-}
-
-static void xu_buffer_dealloc(PyObject *self)
-{
-    xu_buffer_object *xub = (xu_buffer_object *)self;
-    free(xub->buf);
-    PyObject_Del(self);
-}
-
-static PyTypeObject xu_buffer_type = {
-    PyObject_HEAD_INIT(&PyType_Type)
-    0,
-    "buffer",
-    sizeof(xu_buffer_object),
-    0,
-    xu_buffer_dealloc,   /* tp_dealloc     */
-    NULL,                /* tp_print       */
-    xu_buffer_getattr,   /* tp_getattr     */
-    NULL,                /* tp_setattr     */
-    NULL,                /* tp_compare     */
-    NULL,                /* tp_repr        */
-    NULL,                /* tp_as_number   */
-    NULL,                /* tp_as_sequence */
-    NULL,                /* tp_as_mapping  */
-    NULL                 /* tp_hash        */
-};
-
-
-
-/*
- * *********************** MODULE WRAPPER ***********************
- */
-
-static void handle_child_death(int dummy)
-{
-    while ( waitpid(-1, NULL, WNOHANG) > 0 )
-        continue;
-}
-
-static PyObject *xu_autoreap(PyObject *self, PyObject *args)
-{
-    struct sigaction sa;
-
-    if ( !PyArg_ParseTuple(args, "") )
-        return NULL;
-
-    memset(&sa, 0, sizeof(sa));
-    sa.sa_handler = handle_child_death;
-    sigemptyset(&sa.sa_mask);
-    sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
-    (void)sigaction(SIGCHLD, &sa, NULL);
-
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-static PyMethodDef xu_methods[] = {
-    { "notifier", xu_notifier_new, METH_VARARGS, 
-      "Create a new notifier." },
-    { "message", xu_message_new, METH_VARARGS, 
-      "Create a new communications message." },
-    { "port", xu_port_new, METH_VARARGS, 
-      "Create a new communications port." },
-    { "buffer", xu_buffer_new, METH_VARARGS, 
-      "Create a new ring buffer." },
-    { "autoreap", xu_autoreap, METH_VARARGS,
-      "Ensure that zombie children are automatically reaped by the OS." },
-    { NULL, NULL, 0, NULL }
-};
-
-PyMODINIT_FUNC initxend_utils(void)
-{
-    PyObject *m, *d;
-
-    m = Py_InitModule("xend_utils", xu_methods);
-
-    d = PyModule_GetDict(m);
-    port_error = PyErr_NewException("xend_utils.PortError", NULL, NULL);
-    PyDict_SetItemString(d, "PortError", port_error);
-}